package com.xymtop.api.controller;


import com.xymtop.api.config.ServerStatus;
import com.xymtop.api.entity.MyFile;

import com.xymtop.api.exception.IFile.*;
import com.xymtop.api.service.MyFileService;
import com.xymtop.api.utils.JsonResoult;
import com.xymtop.api.utils.Timer;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;

/**
 * @ClassName : MyFileController
 * @Description : 文件操作控制器
 * @Author : 肖叶茂
 * @Date: 2022/12/1  10:48
 */

@RestController
@RequestMapping("/file")
public class MyFileController {
    //设置文件的上传限制
    @Value("${spring.servlet.multipart.max-file-size}")
    private String fileMaxSize;


    @Autowired
    private MyFileService myFileService;

    @RequestMapping("/upload")
    public JsonResoult<MyFile> uploadFile(@RequestParam("file") MultipartFile file,
                                          @RequestParam("contentid") String contentid,
                                          @RequestParam("contenttype") String contenttype){
        //在保存文件之前对文件做检查
        //判断文件是否为空
        if (file.isEmpty()){
            throw new IFileEmptyException("上传文件为空，上传失败");
        }

        //判断文件是否超过限制
        if (file.getSize() >Integer.valueOf(100000000) ){
            throw new IFileSizeException("文件过大，上传失败");
        }

        //判断上传的文件是否为图片类型 file.getContentType()获取的是这种形式 --> text/html
        if (!file.getContentType().contains(file.getContentType())){
            throw new IFileTypeNotMatchException("文件类型不符，上传失败");
        }

        //获取上传文件的原始名字
        String originalFilename = file.getOriginalFilename();

        //获取文件的后缀名
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        //使用时间戳为文件定义新的名字
        String uuidName = Timer.getTimeStamp().toString();

        //定义文件最终的名字
        String fileName = uuidName + suffix;

        //获取项目在服务器上的真实位置并拼凑文件最终的保存位置
        String realPath = System.getProperty("user.dir") + "/src/main/resources/static/upload/" + fileName;

        /**
         * 这里其实有两个选择：①直接将文件写入硬盘 这里选择这种
         * ②先获取存储文件的文件夹的路径，判断存储文件的文件夹是否存在，不存在则创建
         * 最后再将文件名和文件夹路径进行拼凑出最终文件的保存路径
         **/
        //虚拟创建目标文件
        File destFile = new File(realPath);

        //获取目标文件的上级目录
        File parentFile = destFile.getParentFile();

        if (!parentFile.exists()){
            //代表文件的上级目录不存在，进行创建
            parentFile.mkdirs();
        }

        //选择第一种方法，直接写入目标位置
        try {
            file.transferTo(destFile);
        }catch (IFileStatusException e) {
            throw new IFileStatusException("文件状态异常，写入失败");
        }catch (IOException e) {
            throw new IFileUploadIOException("服务器或数据库写入文件失败");
        }



        //将文件的下载路径写入数据库
        String fileAccessPath = "/upload/" + fileName;

        MyFile myFile = new MyFile();
        String id  = Timer.getTimeStamp().toString();

        myFile.setId(id);
        myFile.setStatus("1");
        myFile.setType(suffix.substring(1,suffix.length()));
        myFile.setUrl(fileAccessPath);
        myFile.setContentid(contentid);
        myFile.setContenttype(contenttype);
//        写入数据库
       if(myFileService.upLoadFile(myFile)){
           return new JsonResoult<MyFile>(ServerStatus.SUCCESS,"",myFile);
       }
        return new JsonResoult<MyFile>(ServerStatus.Fail);

    }

    @RequestMapping("/delete")
    public JsonResoult<Void>  deleteFile(@RequestParam("id") String id){
      if ( myFileService.deleteFile(id)){
          return  new JsonResoult<>(ServerStatus.SUCCESS);
      }else {
          return  new JsonResoult<>(ServerStatus.Fail);
      }
    }

    @RequestMapping("/getfile")
    public  JsonResoult<MyFile> getFileById(@RequestParam("id") String id){
        MyFile file =  myFileService.getFileById(id);

        if(file!=null){
            return new JsonResoult<>(ServerStatus.SUCCESS,"",file);
        }else {
            return new JsonResoult<>(ServerStatus.Fail);
        }


    }


    /**
     * @param id     想要下载的文件的id
     * @param response
     * @功能描述 下载文件:
     */
    @RequestMapping("/download")
    public JsonResoult<String> download(@RequestParam("id") String id, HttpServletResponse response) throws IOException {
         MyFile myFile = myFileService.getFileById(id);
         if(myFile!=null){
            try {
                // path是指想要下载的文件的路径
                File file = ResourceUtils.getFile("classpath:static"+myFile.getUrl());
                // 获取文件名
                String filename = file.getName();

                // 将文件写入输入流
                FileInputStream fileInputStream = new FileInputStream(file);
                InputStream fis = new BufferedInputStream(fileInputStream);
                byte[] buffer = new byte[fis.available()];
                fis.read(buffer);
                fis.close();

                // 清空response
                response.reset();
                // 设置response的Header
                response.setCharacterEncoding("UTF-8");
                //Content-Disposition的作用：告知浏览器以何种方式显示响应返回的文件，用浏览器打开还是以附件的形式下载到本地保存
                //attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
                // filename表示文件的默认名称，因为网络传输只支持URL编码的相关支付，因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
                // 告知浏览器文件的大小
                response.addHeader("Content-Length", "" + file.length());
                OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
                response.setContentType("application/octet-stream");
                outputStream.write(buffer);
                outputStream.flush();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return  new JsonResoult<>(200,"");
        }else {
            return  new JsonResoult<>(404,"file not found","");
         }
    }





}
